跳到主要内容

本地数据库为什么放主进程?better-sqlite3 怎么用?

考察点​

  • 原生模块和渲染进程隔离
  • SQLite 放哪、怎么避免卡住 UI

参考答案​

1. 为什么在主进程​

better-sqlite3 是同步 API 加 .node。放渲染进程意味着:

  • 要打开 nodeIntegration 或在 preload 里关 sandbox,页面就能碰到数据库文件
  • 同步查询卡的是页面线程
  • 多窗口会各自加载一份原生模块,锁不好控

数据库文件放在 app.getPath('userData'),只由主进程打开。页面通过 invoke 要结果:

ipcMain.handle('task:list', (event, query) => {
const keyword = String(query.keyword ?? '').slice(0, 100);
// 预编译语句,不要把 keyword 拼进 SQL
return db.prepare('select id, title from task where title like ? limit 50')
.all(`%${keyword}%`);
});

2. 同步库为什么还能用​

better-sqlite3 的同步调用在主进程里确实占事件循环。控制方式:

  • 查询一定有 limit,列表分页
  • 建索引,避免每次全表扫描
  • 大批量导入放到 utilityProcess,主进程只收“导入完成”
  • 单条按主键读取通常是亚毫秒,可以留在主进程

不要在渲染进程用 sql.js 把整个库读进内存,除非数据小到几 MB 而且只读。

3. 多进程和打包​

同一文件不要让主进程和 utility 进程一起写。要拆出去就只让一个进程持有连接。

打包时这个 .node 要 electron-rebuild,并且 asarUnpack。开发机用系统 Node 编译的二进制,放进 Electron 会报 ABI 不对。

4. 退出和损坏​

before-quit 里把连接关掉。打开数据库如果发现损坏,先把文件复制备份再决定是否重建,不要直接删掉用户数据。

面试话术​

本地库放 userData,连接只开在主进程,页面用 invoke 分页查询,SQL 用占位符。better-sqlite3 是同步的,所以大导入放 utilityProcess,常规按主键读取留在主进程。原生模块按 Electron ABI 重编译并 unpack。多进程不要同时写同一个文件。

常见追问​

  • 为什么不用 IndexedDB? 适合页面自己的缓存。要备份、要给主进程和其他窗口共用、要复杂查询时,SQLite 更合适。
  • 加密数据库? 可以用 SQLCipher 一类扩展,密钥放 safeStorage。加密代替不了“页面不能直接打开文件”这道边界。